package com.example.socket.codec;

import com.example.socket.anno.impl.InBodyParameter;
import com.example.socket.exception.DecodeException;
import com.example.socket.exception.ParameterException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;

import java.io.IOException;
import java.lang.reflect.Type;

/**
 * @author frank
 * <code>JSON</code>格式的编解码器
 */
public class JsonCoder extends CoderSupport {

    private static final ObjectMapper mapper = new ObjectMapper();
    private static final TypeFactory typeFactory = TypeFactory.defaultInstance();

    @Override
    protected byte[] doEncode(Object obj, Type type) {
        try {
            if (obj instanceof byte[]) {
                return (byte[]) obj;
            }
            return mapper.writeValueAsBytes(obj);
        } catch (Exception e) {
            String message = "JSON编码失败:" + e.getMessage();
            if (logger.isErrorEnabled()) {
                logger.info(message, e);
            }
            throw new DecodeException(message, e);
        }
    }

    @Override
    protected Object doDecode(byte[] bytes, Type type) {
        if (type.equals(void.class)) {
            return null;
        }
        if (ArrayUtils.isEmpty(bytes)) {
            return null;
        }
        try {
            if (type instanceof JavaType) {
                return decodeByJavaType(bytes, (JavaType) type);
            } else if (type instanceof Type) {
                return decodeByType(bytes, type);
            }
        } catch (Exception e) {
            String message = "JSON解码失败:" + e.getMessage();
            if (logger.isErrorEnabled()) {
                logger.info(message);
            }
            throw new DecodeException(message, e);
        }

        FormattingTuple message = MessageFormatter.format("不支持的类型参数[{}]", type);
        if (logger.isErrorEnabled()) {
            logger.info(message.getMessage());
        }
        throw new IllegalArgumentException(message.getMessage());
    }

    /** 使用 {@link Type} 描述进行解码 */
    private Object decodeByType(byte[] bytes, Type type) throws JsonParseException, JsonMappingException, IOException {
        return mapper.readValue(bytes, 0, bytes.length, typeFactory.constructType(type));
    }

    /** 使用 {@link JavaType} 描述进行解码 */
    private Object decodeByJavaType(byte[] bytes, JavaType type) throws JsonParseException, JsonMappingException,
            IOException {
        return mapper.readValue(bytes, 0, bytes.length, type);
    }

    @Override
    public Object getInBody(Object body, InBodyParameter parameter) {
        JsonNode node = (JsonNode) body;
        JsonParser parser;
        if (node.isArray()) {
            int index = Integer.parseInt(parameter.getValue());
            if (node.has(index)) {
                parser = node.get(index).traverse();
            } else if (parameter.isRequired()) {
                FormattingTuple message = MessageFormatter.format("注释[{}]请求的参数不存在", parameter.getAnnotation());
                if (logger.isErrorEnabled()) {
                    logger.info(message.getMessage());
                }
                throw new ParameterException(message.getMessage());
            } else {
                return null;
            }
        } else {
            String name = parameter.getValue();
            if (node.has(name)) {
                parser = node.get(name).traverse();
            } else if (parameter.isRequired()) {
                FormattingTuple message = MessageFormatter.format("注释[{}]请求的参数不存在", parameter.getAnnotation());
                if (logger.isErrorEnabled()) {
                    logger.info(message.getMessage());
                }
                throw new ParameterException(message.getMessage());
            } else {
                return null;
            }
        }
        parser.setCodec(mapper);
        try {
            JavaType javaType = typeFactory.constructType(parameter.getType());
            return parser.getCodec().readValue(parser, javaType);
        } catch (Exception e) {
            String message = "处理InBody数据提取时出现异常";
            if (logger.isErrorEnabled()) {
                logger.info(message, e);
            }
            throw new ParameterException(message, e);
        }
    }

    @Override
    public Type getDefaultDecodeType() {
        return JsonNode.class;
    }

    @Override
    public Type getDefaultEncodeType() {
        return null;
    }

    @Override
    public Coder getClone() {
        return this;
    }

}
